<template>
  <li :class="getClass">
    <template v-if="!getCollapse">
      <div :class="`${prefixCls}-submenu-title`" @click.stop="handleClick" :style="getItemStyle">
        <slot name="title"></slot>
        <Icon
          icon="eva:arrow-ios-downward-outline"
          :size="14"
          :class="`${prefixCls}-submenu-title-icon`"
        />
      </div>
      <CollapseTransition>
        <ul :class="prefixCls" v-show="opened">
          <slot></slot>
        </ul>
      </CollapseTransition>
    </template>

    <Popover
      placement="right"
      :overlayClassName="`${prefixCls}-menu-popover`"
      v-else
      :visible="getIsOpend"
      @visible-change="handleVisibleChange"
      :overlayStyle="getOverlayStyle"
      :align="{ offset: [0, 0] }"
    >
      <div :class="getSubClass" v-bind="getEvents(false)">
        <div
          :class="[
            {
              [`${prefixCls}-submenu-popup`]: !getParentSubMenu,
              [`${prefixCls}-submenu-collapsed-show-tit`]: collapsedShowTitle,
            },
          ]"
        >
          <slot name="title"></slot>
        </div>
        <Icon
          v-if="getParentSubMenu"
          icon="eva:arrow-ios-downward-outline"
          :size="14"
          :class="`${prefixCls}-submenu-title-icon`"
        />
      </div>
      <!-- eslint-disable-next-line -->
      <template #content v-show="opened">
        <div v-bind="getEvents(true)">
          <ul :class="[prefixCls, `${prefixCls}-${getTheme}`, `${prefixCls}-popup`]">
            <slot></slot>
          </ul>
        </div>
      </template>
    </Popover>
  </li>
</template>

<script lang="ts">
import type { CSSProperties, PropType } from "vue";
import type { SubMenuProvider } from "./Types";
import {
  defineComponent,
  computed,
  unref,
  getCurrentInstance,
  toRefs,
  reactive,
  provide,
  onBeforeMount,
  inject
} from "vue";
import { useDesign } from "/@/hooks/web/UseDesign";
import { propTypes } from "/@/utils/PropTypes";
import { useMenuItem } from "./UseMenu";
import { useSimpleRootMenuContext } from "./UseSimpleMenuContext";
import { CollapseTransition } from "/@/components/general/Transition";
import { Icon } from "/@/components/general/Icon";
import { Popover } from "ant-design-vue";
import { isBoolean, isObject } from "/@/utils/Is";
import mitt from "/@/utils/Mitt";

const DELAY = 200;
export default defineComponent({
  name: "SubMenu",
  components: {
    Icon,
    CollapseTransition,
    Popover
  },
  props: {
    name: {
      type: [String, Number] as PropType<string | number>,
      required: true
    },
    disabled: propTypes.bool,
    collapsedShowTitle: propTypes.bool
  },
  setup(props) {
    const instance = getCurrentInstance();

    const state = reactive({
      active: false,
      opened: false
    });

    const data = reactive({
      timeout: null as TimeoutHandle | null,
      mouseInChild: false,
      isChild: false
    });

    const { getParentSubMenu, getItemStyle, getParentMenu, getParentList } =
      useMenuItem(instance);

    const { prefixCls } = useDesign("menu");

    const subMenuEmitter = mitt();

    const { rootMenuEmitter } = useSimpleRootMenuContext();

    const {
      addSubMenu: parentAddSubmenu,
      removeSubMenu: parentRemoveSubmenu,
      removeAll: parentRemoveAll,
      getOpenNames: parentGetOpenNames,
      isRemoveAllPopup,
      sliceIndex,
      level,
      props: rootProps,
      handleMouseleave: parentHandleMouseleave
    } = inject<SubMenuProvider>(`subMenu:${getParentMenu.value?.uid}`)!;

    const getClass = computed(() => {
      return [
        `${prefixCls}-submenu`,
        {
          [`${prefixCls}-item-active`]: state.active,
          [`${prefixCls}-opened`]: state.opened,
          [`${prefixCls}-submenu-disabled`]: props.disabled,
          [`${prefixCls}-submenu-has-parent-submenu`]: unref(getParentSubMenu),
          [`${prefixCls}-child-item-active`]: state.active
        }
      ];
    });

    const getAccordion = computed(() => rootProps.accordion);
    const getCollapse = computed(() => rootProps.collapse);
    const getTheme = computed(() => rootProps.theme);

    const getOverlayStyle = computed((): CSSProperties => {
      return {
        minWidth: "200px"
      };
    });

    const getIsOpend = computed(() => {
      const name = props.name;
      if (unref(getCollapse)) {
        return parentGetOpenNames().includes(name);
      }
      return state.opened;
    });

    const getSubClass = computed(() => {
      const isActive = rootProps.activeSubMenuNames.includes(props.name);
      return [
        `${prefixCls}-submenu-title`,
        {
          [`${prefixCls}-submenu-active`]: isActive,
          [`${prefixCls}-submenu-active-border`]: isActive && level === 0,
          [`${prefixCls}-submenu-collapse`]: unref(getCollapse) && level === 0
        }
      ];
    });

    function getEvents(deep: boolean) {
      if (!unref(getCollapse)) {
        return {};
      }
      return {
        onMouseenter: handleMouseenter,
        onMouseleave: () => handleMouseleave(deep)
      };
    }

    function handleClick() {
      const { disabled } = props;
      if (disabled || unref(getCollapse)) return;
      const opened = state.opened;

      if (unref(getAccordion)) {
        const { uidList } = getParentList();
        rootMenuEmitter.emit("on-update-opened", {
          opend: false,
          parent: instance?.parent,
          uidList: uidList
        });
      } else {
        rootMenuEmitter.emit("open-name-change", {
          name: props.name,
          opened: !opened
        });
      }
      state.opened = !opened;
    }

    function handleMouseenter() {
      const disabled = props.disabled;
      if (disabled) return;

      subMenuEmitter.emit("submenu:mouse-enter-child");

      const index = parentGetOpenNames().findIndex((item) => item === props.name);

      sliceIndex(index);

      const isRoot = level === 0 && parentGetOpenNames().length === 2;
      if (isRoot) {
        parentRemoveAll();
      }
      data.isChild = parentGetOpenNames().includes(props.name);
      clearTimeout(data.timeout!);
      data.timeout = setTimeout(() => {
        parentAddSubmenu(props.name);
      }, DELAY);
    }

    function handleMouseleave(deepDispatch = false) {
      const parentName = getParentMenu.value?.props.name;
      if (!parentName) {
        isRemoveAllPopup.value = true;
      }

      if (parentGetOpenNames().slice(-1)[0] === props.name) {
        data.isChild = false;
      }

      subMenuEmitter.emit("submenu:mouse-leave-child");
      if (data.timeout) {
        clearTimeout(data.timeout!);
        data.timeout = setTimeout(() => {
          if (isRemoveAllPopup.value) {
            parentRemoveAll();
          } else if (!data.mouseInChild) {
            parentRemoveSubmenu(props.name);
          }
        }, DELAY);
      }
      if (deepDispatch) {
        if (getParentSubMenu.value) {
          parentHandleMouseleave?.(true);
        }
      }
    }

    onBeforeMount(() => {
      subMenuEmitter.on("submenu:mouse-enter-child", () => {
        data.mouseInChild = true;
        isRemoveAllPopup.value = false;
        clearTimeout(data.timeout!);
      });
      subMenuEmitter.on("submenu:mouse-leave-child", () => {
        if (data.isChild) return;
        data.mouseInChild = false;
        clearTimeout(data.timeout!);
      });

      rootMenuEmitter.on(
        "on-update-opened",
        (data: boolean | (string | number)[] | Recordable) => {
          if (unref(getCollapse)) return;
          if (isBoolean(data)) {
            state.opened = data;
            return;
          }
          if (isObject(data) && rootProps.accordion) {
            const { opend, parent, uidList } = data as Recordable;
            if (parent === instance?.parent) {
              state.opened = opend;
            } else if (!uidList.includes(instance?.uid)) {
              state.opened = false;
            }
            return;
          }

          if (props.name && Array.isArray(data)) {
            state.opened = (data as (string | number)[]).includes(props.name);
          }
        }
      );

      rootMenuEmitter.on("on-update-active-name:submenu", (data: number[]) => {
        if (instance?.uid) {
          state.active = data.includes(instance?.uid);
        }
      });
    });

    function handleVisibleChange(visible: boolean) {
      state.opened = visible;
    }

    // provide
    provide<SubMenuProvider>(`subMenu:${instance?.uid}`, {
      addSubMenu: parentAddSubmenu,
      removeSubMenu: parentRemoveSubmenu,
      getOpenNames: parentGetOpenNames,
      removeAll: parentRemoveAll,
      isRemoveAllPopup,
      sliceIndex,
      level: level + 1,
      handleMouseleave,
      props: rootProps
    });

    return {
      getClass,
      prefixCls,
      getCollapse,
      getItemStyle,
      handleClick,
      handleVisibleChange,
      getParentSubMenu,
      getOverlayStyle,
      getTheme,
      getIsOpend,
      getEvents,
      getSubClass,
      ...toRefs(state),
      ...toRefs(data)
    };
  }
});
</script>
